﻿/*********************************************************************************
 *Author:         OnClick
 *Version:        1.0
 *UnityVersion:   2018.3.1f1
 *Date:           2019-03-26
 *Description:    IFramework
 *History:        2018.11--
*********************************************************************************/
using System;
using System.Text;
using System.Threading;

namespace IFramework
{
    public class WSClientToken : IDisposable
    {
        public SockDel.OnConnect OnConnected { get; set; }
        private TcpClientToken clientToken;
        private Encoding encoding = Encoding.UTF8;
        private ManualResetEvent resetEvent = new ManualResetEvent(false);
        private AcceptInfo acceptInfo;
        private int waitTimeout = 1000 * 60 * 30;

        public bool IsConnected { get; private set; }
        public SockDel.OnDisConnect onDisConnect { get; set; }
        public SockDel.OnReceieve onReceive { get; set; }
        public SockDel.OnSendCallBack onSendCallBack { get; set; }
        public WSClientToken(int bufferSize = 4096, int blocks = 8)
        {
            clientToken = new TcpClientToken(bufferSize, blocks);
            clientToken.onDisConnect += new SockDel.OnDisConnect(OnDisConnect);
            clientToken.onReceive += new SockDel.OnReceieve(OnReceive);
            clientToken.onSendCallBack += new SockDel.OnSendCallBack(OnSendCallBack);
        }
        public void Dispose()
        {
            resetEvent.Dispose();
        }
        public bool Connect(string wsUrl)
        {
            string[] urlParams = wsUrl.Split(':');
            if (wsUrl.Length < 3)
                throw new Exception("wsUrl is error format.for example as ws://localhost:80");
            urlParams[1] = urlParams[1].Replace("//", "");
            System.Random rand = new System.Random(DateTime.Now.Millisecond);
            string host = urlParams[1] + ":" + urlParams[2];
            bool isOk = clientToken.ConnectTo(int.Parse(urlParams[2]), urlParams[1]);
            if (isOk == false) throw new Exception("连接失败...");
            string req = new AccessInfo()
            {
                Host = host,
                Origin = "http://" + host,
                SecWebSocketKey = Convert.ToBase64String(encoding.GetBytes(wsUrl + rand.Next(100, 100000).ToString()))
            }.ToString();
            byte[] buffer = encoding.GetBytes(req);
            isOk = clientToken.SendAsync(buffer, 0, buffer.Length);
            resetEvent.WaitOne(waitTimeout);
            return IsConnected;
        }
        public bool Connect(UrlInfo url)
        {
            return Connect(string.Format("{0}://{1}:{2}", url.Proto, url.Domain, url.Port));
        }

        public bool Send(string msg, bool waiting = true)
        {
            if (IsConnected == false) return false;
            var buf = new ClientPackage().GetBytes(msg);
            clientToken.SendAsync(buf, 0, buf.Length, waiting);
            return true;
        }
        public bool Send(BufferSegment data, bool waiting = true)
        {
            if (IsConnected == false) return false;
            var pac = new ClientPackage();
            pac.Data = data;
            pac.DataLength = data.Len;
            var buf = pac.EncodingToBytes();
            clientToken.SendAsync(buf, 0, buf.Length, waiting);
            return true;
        }

        private void OnDisConnect(SocketToken sToken)
        {
            IsConnected = false;
            if (onDisConnect != null) onDisConnect(sToken);
        }

        private void OnReceive(SocketToken token, byte[] buff, int offset, int len)
        {
            if (!IsConnected)
            {
                string msg = encoding.GetString(buff, offset, len);
                acceptInfo = new ClientPackage().GetAcceptPackage(msg);
                if ((IsConnected = acceptInfo.IsHandShaked))
                {
                    resetEvent.Set();
                    if (OnConnected != null) OnConnected(token, IsConnected);
                }
                else
                {
                    clientToken.DisConnect();
                }
            }
            else
            {
                ClientPackage packet = new ClientPackage();
                packet.DecodingFromBytes(new BufferSegment(buff, offset, len), true);
                if (packet.OpCode == 0x01)
                {
                    //if (OnReceived != null)
                    //    OnReceived(token, encoding.GetString(packet.Data.buffer,
                    //    packet.Data.offset, packet.Data.Len));
                }
                else if (packet.OpCode == 0x08)
                {
                    IsConnected = false;
                    clientToken.DisConnect();
                }
                else
                {
                    if (onReceive != null)
                        onReceive(token, packet.Data.Buffer,
                        packet.Data.Offset, packet.Data.Len);
                }
            }
        }
        private void OnSendCallBack(SocketToken token, byte[] buff, int offset, int len)
        {
            if (onSendCallBack != null)
            {
                onSendCallBack(token, buff, offset, len);
            }
        }
    }

}
